package com.godenwater.recv.handler;

import java.text.SimpleDateFormat;
import java.util.Arrays;

import com.godenwater.recv.model.CommonMessage;
import com.godenwater.recv.utils.ByteUtils;
import org.apache.mina.core.session.IoSession;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.godenwater.yanyu.YYBuilder;
import com.godenwater.yanyu.YYParser;
import com.godenwater.yanyu.utils.ByteUtil;
import com.godenwater.yanyu.utils.CRC16Helper;
import com.godenwater.recv.RecvConstant;
import com.godenwater.recv.server.all.RtuServer;

public class YanyuMessageHandler extends AbstractHandler {

    private Logger logger = LoggerFactory.getLogger(YanyuMessageHandler.class);

    private SimpleDateFormat sdf = new SimpleDateFormat("yyMMddHHmmss");

    public static YanyuMessageHandler getInstance() {
        return YanyuMessageHandlerContainer.instance;
    }

    // Wrap this guy up so we can mock out the UserManager class.
    private static class YanyuMessageHandlerContainer {
        private static YanyuMessageHandler instance = new YanyuMessageHandler();
    }

    public void perform(String channel, IoSession session,
                        CommonMessage message, int recvPort) {

        com.godenwater.yanyu.IMessageHeader header = (com.godenwater.yanyu.IMessageHeader) message
                .getHeader();

        byte[] funcCode = header.getFuncCode();
        byte mode = message.getEOF();
        byte[] stationAddr = header.getStationAddr();
        byte[] centerAddr = header.getCenterAddr();
        //String stcd = "" + YYParser.parseStcd(centerAddr, stationAddr);// 此处有问题，若是多个测站共用一个测站编码，会出现召测不到信息的情况。

        // 3、应答回复或重发报文请求
        byte[] replyMsg = null;

        // 1、 校验消息
        boolean crcFlag = checkCRC(message);// 校验消息体，CRC校验

        // 2、 存放于消息队列中，等待处理。将队列放在此处，减少了对链接报的处理
        // 2、将消息存放在日志文件中 2016-10-18
        /**
         ChannelMessage cm = new ChannelMessage();
         cm.setChannel(channel);
         cm.setMessage(message);
         cm.setCrcFlag(crcFlag);
         // server.queuePush(cm);
         RtuServer.getInstance().getMessageManager().append(Constant.YY, cm);
         */

        // 3、回复消息
        if (crcFlag) {
            // 确认应答
            logger.info("校验结果，报文CRC验证<相一致>，M2模式发送\"应答\"回复....");
            replyMsg = replyMessage(session, message);
        } else {
            // 重发请求
            logger.info("校验结果，报文CRC验证\"不一致 \"，M2模式发送\"重发\"请求....");
            //replyMsg = repeatMessage(message);
            replyMsg = replyMessage(session, message);
        }

        if (replyMsg != null && (channel.equalsIgnoreCase("GPRS") || channel.equalsIgnoreCase("UDP"))) {
            logger.info("回复报文，" + ByteUtil.toHexString(replyMsg));
            session.write(replyMsg);
        }

        // 4、将测站与session关联起来
        // String stcd = ByteUtils.bytesToHexString(header.getCenterAddr()) + "_" + ByteUtils.bytesToHexString(header.getStationAddr());
        String stcd = "" + YYParser.parseStcd(centerAddr, stationAddr);
        RtuServer.getInstance().getSessionManager().bindYYSession(session, stcd, ByteUtils.bytesToHexString(header.getCenterAddr()), ByteUtils.bytesToHexString(stationAddr));

        // 5、追加畅通率计算
        //rateMessage(channel, funcCode[0], stcd);

        // 6、监测器显示
        String logMsg = viewMessage(message);
        monitorMessage(channel, stcd, logMsg);

        // 2.2 写入报文记录
        saveMessage(channel, RecvConstant.YY, stcd, crcFlag, logMsg, "0", "", "", 0, "",0);
    }

    public boolean checkCRC(CommonMessage message) {
        com.godenwater.yanyu.IMessageHeader header = (com.godenwater.yanyu.IMessageHeader) message
                .getHeader();
        byte[] body = message.getContent();

        //logger.info("校验报文，开始校验CRC...  ");

        byte[] bytes = new byte[header.getLength() + body.length + 1];

        int pos = 0;
        System.arraycopy(header.getStartBit(), 0, bytes, pos,
                header.getStartBit().length);
        pos = pos + header.getStartBit().length; // 此处将取值直接改为取数据中字节的长度，对字节处理更精确

        System.arraycopy(header.getCenterAddr(), 0, bytes, pos,
                header.getCenterAddr().length);
        pos = pos + header.getCenterAddr().length;

        System.arraycopy(header.getStationAddr(), 0, bytes, pos,
                header.getStationAddr().length);
        pos = pos + header.getStationAddr().length;

        System.arraycopy(header.getFuncCode(), 0, bytes, pos,
                header.getFuncCode().length);
        pos = pos + header.getFuncCode().length;

        System.arraycopy(header.getBodySize(), 0, bytes, pos,
                header.getBodySize().length);
        pos = pos + header.getBodySize().length;

        System.arraycopy(body, 0, bytes, pos, body.length);
        pos = pos + body.length;

        // System.arraycopy(message.getEOF(), 0, bytes, pos, 1);
        bytes[pos] = message.getEOF();

        byte[] crcResult = CRC16Helper.crc16Check(bytes);

        // System.out.println(">> crcResult " +
        // ByteUtil.toHexString(crcResult));

        if (Arrays.equals(crcResult, message.getCRC())) {
            return true;
        } else {
            return false;
        }
    }

    public byte[] replyMessage(IoSession session, CommonMessage message) {
        try {
            com.godenwater.yanyu.IMessageHeader header = (com.godenwater.yanyu.IMessageHeader) message
                    .getHeader();

            // 报文回复
            byte[] funcCode = header.getFuncCode();

            com.godenwater.yanyu.command.DownCommand cmd = new com.godenwater.yanyu.command.DownCommand();
            cmd.setStartBit(header.getStartBit()[0]);
            cmd.setCenterAddr(header.getCenterAddr());
            cmd.setStationAddr(header.getStationAddr());
            cmd.setFuncCode(funcCode);

            // 2、判断结束符是哪种格式
            // 2.1 ：在报文分包传输时作为结束符，表示未完成，不可退出通信
            if (message.getEOF() == com.godenwater.yanyu.Symbol.ETB) {
                // ACK 肯定确认，继续发送，作为有后续报文帧的“确认”结束符。
                // NAK 否定应答，反馈重发， 用于要求对方重发某数据包的报文结束符。
                // ENQ 作为下行查询及控制命令帧的报文结束符。

                cmd.setEof(com.godenwater.yanyu.Symbol.ACK);

                return YYBuilder.toByte(cmd.sendAckMessage(true), cmd.getUpDown());
            }

            // 2.2：作为报文结束符，表示传输完成等待退出通信，
            if (message.getEOF() == com.godenwater.yanyu.Symbol.ETX) {
                // --- 反馈用EOT 作为传输结束确认帧报文符，表示可以退出通信。
                // --- ESC 要求终端在线。保持在线10分钟内若没有接收到中心站命令，终端退回原先设定的工作状态
                cmd.setEof(com.godenwater.yanyu.Symbol.EOT);

                // 判断是否有召测信息，如果有，则需要将遥测站保持在线10分钟，见协议：6.2.2（2015-09-06新添加）
                byte xstcd1 = (byte) (header.getCenterAddr()[0] & 0x0F);
                int xstcd = com.godenwater.yanyu.utils.ByteUtil.bytesToUshort(new byte[]{xstcd1, header.getStationAddr()[0]});
                int downCmdLen = checkCommand("YY", String.valueOf(xstcd));//这个需要改变为，燕禹的协议有借位的现象
                //int downCmdLen = checkCommand("YY","",ByteUtils.bytesToHexString(header.getCenterAddr()),ByteUtils.bytesToHexString(header.getStationAddr()));//这个需要改变为，燕禹的协议有借位的现象
                if (downCmdLen > 0) {
                    cmd.setEof(com.godenwater.yanyu.Symbol.ESC);
                    // 表示有下发的报文，需要进行下发
                    RtuServer.getInstance().getSessionManager().bindCaller(session, ByteUtils.bytesToHexString(header.getStationAddr()), "1");
                }
                /**
                 boolean haveCaller = StationManager.getInstance().getCaller(
                 stcd);
                 if (haveCaller) {
                 cmd.setEof(com.easywater.yanyu.Symbol.ENQ);
                 SessionManager.getInstance()
                 .bindCaller(session, stcd, true);
                 } else {
                 cmd.setEof(com.easywater.yanyu.Symbol.EOT);
                 SessionManager.getInstance().bindCaller(session, stcd,
                 false);
                 }
                 */
            }

            return YYBuilder.toByte(cmd.sendReplyMessage(true), cmd.getUpDown());

        } catch (Exception e) {
            e.printStackTrace();
            logger.info(">> 构造回复报文出现异常！" + e.getMessage());
            return null;
        }
    }

    public byte[] repeatMessage(CommonMessage message) {
        try {
            com.godenwater.yanyu.IMessageHeader header = (com.godenwater.yanyu.IMessageHeader) message
                    .getHeader();

            // 报文回复
            com.godenwater.yanyu.command.DownCommand cmd = new com.godenwater.yanyu.command.DownCommand();
            cmd.setStartBit(header.getStartBit()[0]);
            cmd.setCenterAddr(header.getCenterAddr());
            cmd.setStationAddr(header.getStationAddr());
            cmd.setFuncCode(header.getFuncCode());
            cmd.setEof(com.godenwater.yanyu.Symbol.NAK);

            return YYBuilder.toByte(cmd.sendReplyMessage(true), cmd.getUpDown());

        } catch (Exception e) {
            e.printStackTrace();
            logger.info(">> 构造回复报文出现异常！" + e.getMessage());
            return null;
        }
    }

    /**
     * 计算畅通率
     *
     * @param message
     * @return
     */
    public void rateMessage(String channel, byte funcCode, String stcd) {
        try {

            if (funcCode == 0x0E) {
                //RtuServer.getInstance().getStationManager()
                //		.updateRate(channel, stcd);
            }

        } catch (Exception e) {
            e.printStackTrace();
            logger.info(">> 计算申通率出现异常！" + e.getMessage());
        }
    }

    /**
     * 将报文转换为HEX字符串
     *
     * @param message
     * @return
     */
    public String viewMessage(CommonMessage message) {

        com.godenwater.yanyu.IMessageHeader header = (com.godenwater.yanyu.IMessageHeader) message
                .getHeader();
        byte[] body = message.getContent();

        logger.debug("开始发送监测信息...  ");

        byte[] bytes = new byte[header.getLength() + body.length + 1];

        int pos = 0;
        System.arraycopy(header.getStartBit(), 0, bytes, pos,
                header.getStartBit().length);
        pos = pos + header.getStartBit().length; // 此处将取值直接改为取数据中字节的长度，对字节处理更精确

        System.arraycopy(header.getCenterAddr(), 0, bytes, pos,
                header.getCenterAddr().length);
        pos = pos + header.getCenterAddr().length;

        System.arraycopy(header.getStationAddr(), 0, bytes, pos,
                header.getStationAddr().length);
        pos = pos + header.getStationAddr().length;

        System.arraycopy(header.getFuncCode(), 0, bytes, pos,
                header.getFuncCode().length);
        pos = pos + header.getFuncCode().length;

        System.arraycopy(header.getBodySize(), 0, bytes, pos,
                header.getBodySize().length);
        pos = pos + header.getBodySize().length;

        System.arraycopy(body, 0, bytes, pos, body.length);
        pos = pos + body.length;

        // System.arraycopy(message.getEOF(), 0, bytes, pos, 1);
        bytes[pos] = message.getEOF();

        String log = ByteUtil.toHexString(bytes)
                + ByteUtil.toHexString(message.getCRC());

        return log;
    }

}
